#!/usr/bin/python
import urllib2, cookielib, mimetools, mimetypes, itertools, simplejson
from xml.dom.minidom import parseString
from xml.etree import ElementTree
import logging

from MuPython.exceptions import MuConnException

log = logging.getLogger('MuPython.MuConnection')

""" MuConnection contains all the necessary classes for establishing a secured 
connection to the Mu Test Suite and will be used by all other classes to 
send and retrieve data from/to the Mu Test Suite.

    Classes contained:
        MuConnection - used to establish and maintin a connection, send data, 
        and retrieve data.

        MuResponseObject - The response class that is returned by all commands 
        in the this API. It is an attempt to reduce impact if any changes in 
        the underlying API occur.

        MultiPartForm - usede for mime attachments on some commands to the Mu 
        and is controled entirely by methods in MuConection.

"""

class MuConnection():
    """ MuConnection: This class is instantiated to establish a secured 
    connection to the Mu Test Suite.  It controls the communication of all 
    classes to the Mu Test Suite.
    
    Arguments: 
        username - the username to use in establishing the connection. 
        default is admin.  currenlty only admin can run the API
        password - the password to use in estabishing a secured connection. 
        default is admin.
    
    Globals:
    
    TODO:
        Need to implement a logging method
    """
    
    TOP_LEVEL_URL = "https://demo.homedns.org"
    USERNAME = "admin"
    PASSWORD = "admin"
    COOKIES = None
    # create a password manager
    PASSWORD_MNGR = None
    AUTH_HANDLER = None
    # create cookie handler
    COOKIE_HANDLER = None
    OPENER = None
    RESPONSE_OBJECT = None
    VERBOSE = True
    
    def __init__(self, mu="", username="admin", password="admin"):
        """ initiate a new MuConnection for testing 
        
        Arguments:
            Mu - the mu ip address or https URL
            username - the username for authenticatoin. normaly admin
            password - the password for authentication
        
        """
        if mu.startswith("https://"):
            #looks like an url so go with that
            self.TOP_LEVEL_URL = mu
        elif mu.startswith("http://"):
            #looks like an url so go with that
            self.TOP_LEVEL_URL = mu
        else:
            #we are lazy and just add https:// to front of value to make an url
            self.TOP_LEVEL_URL = "https://" + mu
        log.debug("Top level URL: %s", self.TOP_LEVEL_URL)
        self.setConnection(self.TOP_LEVEL_URL, username, password)


    def setAuthInfo(self, baseUrl, username="admin", password="admin"):
        """ used internaly by MuConenction for authentication handaling
        
        Arguments:
            baseUrl - the root url for the Mu: https://1.1.1.1 or 
            https://myMu.myDomain
            username - the username for authenticatoin. normaly admin
            password - the password for authentication
        
        """
        self.USERNAME = username
        self.PASSWORD = password
        self.PASSWORD_MNGR = urllib2.HTTPPasswordMgrWithDefaultRealm()
        self.PASSWORD_MNGR.add_password(None, baseUrl, username, password)
        # basic auth handler
        self.AUTH_HANDLER = urllib2.HTTPBasicAuthHandler(self.PASSWORD_MNGR)
        log.debug('Auth credentials set.')
        return

    def setConnection(self, baseUrl, username, password):
        """ used internaly by MuConenction for connection establishment
        
        Arguments:
            baseUrl - the root url for the Mu: https://1.1.1.1 or 
            https://myMu.myDomain
            username - the username for authenticatoin. normaly admin
            password - the password for authentication
        
        """
        self.setAuthInfo(baseUrl, username, password)
        self.COOKIE_HANDLER = urllib2.HTTPCookieProcessor(self.COOKIES)
        # create "opener" (OpenerDirector instance)
        self.OPENER = urllib2.build_opener(
            self.AUTH_HANDLER,self.COOKIE_HANDLER)
        #self.OPENER = urllib2.build_opener(self.AUTH_HANDLER)
        # install opener for use with all requests
        urllib2.install_opener(self.OPENER)
        log.debug('Connection set.')
        return

    def get(self,url,isJsonResponse=False,userAgent=USERNAME):
        """ Get method used to get html reponses and objects from the Mu API 
        sessions
        
        Arguments:
            url - the url used to retrieve data. it is combined with the root 
            url
            isJsonResponse - is the response in the form of a json object
            userAgent - the userAgent to assign the reuest to. normaly the 
            same as the username used in the authentication
        
            Returns:
            MuResponseObject - Data is retrieved from Mu and stored in 
            MuResponseObject.
               COMPLETED - True if operation completed normaly
               ERROR - False if no error occured
               RAW_RESPONSE - Raw data from HTTP get request or URL error 
               if error is thrown
               UUID - contains Mu Job UUID if a UUID is found in XML response 
               from Mu
               JOBID - contains Mu Job ID if a Job Id is found in XML response 
               from Mu
               JSON_OBJCT - conains JSON data if isJsonResponse is true
               RETRIEVED_OBJECT - Fomrated Object of type specified in 
               RETRIEVED_OBJECT_TYPE
               RETRIEVED_OBJECT_TYPE - Can be JSON, XML, TXT, ERROR
               URL - contains text URL used in creation of urllib2.requst
               REQUST - contains urlib2.requst used in operation
               STATUS - JSON status if JSON response is found
               NAME - contains name value if JSON name or xml name data is found
               MESSAGE - JSON message value if JSON response is found
        
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        log.debug("URL: %s", url)
        self.RESPONSE_OBJECT.setUrl(url)
        req = urllib2.Request(url)
        req.add_header("User-Agent", userAgent)
        self.RESPONSE_OBJECT.setRequest(req)
        try:
            buffer = urllib2.urlopen(req)
            if isJsonResponse:
                try:
                    data = buffer.read()
                    data = simplejson.loads(data)
                except:
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    log.debug("Raw Response Data: %s", data)
                    self.processResponse(data,self.RESPONSE_OBJECT,False)
                    return self.RESPONSE_OBJECT
            else:
                data = buffer.read()
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
        self.RESPONSE_OBJECT.setRawResponse(data)
        log.debug("Raw Response Data: %s", data)
        self.processResponse(data,self.RESPONSE_OBJECT,isJsonResponse)
        return self.RESPONSE_OBJECT 

    def getFile(self,url,fileToSave='',fileMode='b',userAgent=USERNAME):
        """ Get method used to get stream of data  and save in a file
        
        Arguments:
            url - the url used to retrieve data. it is combined with the root 
            url
            fileToSave - file name including path to save data in
            fileMode - default is binary (b) but text (t) can be specified
            userAgent - the userAgent to assign the reuest to. normaly the 
            same as the username used in the authentication
            
            Returns:
            MuResponseObject - Data is retrieved from Mu and stored in 
            MuResponseObject
               COMPLETED - True if operation completed normaly
               ERROR - False if no error occured
               RAW_RESPONSE - Raw data from HTTP get request or URL error if 
               error is thrown
               UUID - none
               JOBID - none
               JSON_OBJCT - none
               RETRIEVED_OBJECT - File name used in file write operation
               RETRIEVED_OBJECT_TYPE - FILE
               URL - contains text URL used in creation of urllib2.requst
               REQUST - contains urlib2.requst used in operation
               STATUS - none
               NAME - none
               MESSAGE - none
            
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setUrl(url)
        req = urllib2.Request(url)
        req.add_header("User-Agent", userAgent)
        self.RESPONSE_OBJECT.setRequest(req)
        try:
            # Open the local file for writing
            newFile = open(fileToSave, "w" + fileMode)
            buffer = urllib2.urlopen(req)
            #Write to it from url read
            data = buffer.read()
            newFile.write(data)
            #newFile.write(buffer.read())
            newFile.close()
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
            self.RESPONSE_OBJECT.setError(True)
            self.RESPONSE_OBJECT.setRetrievedObjectType(
                self.RESPONSE_OBJECT.HTTP_ERROR)
            self.RESPONSE_OBJECT.setRetrievedObject(err)
            self.RESPONSE_OBJECT.setCompleted(False)
            raise MuConnException
        self.RESPONSE_OBJECT.setRetrievedObjectType(self.RESPONSE_OBJECT.FILE)
        self.RESPONSE_OBJECT.setRetrievedObject(fileToSave)
        self.RESPONSE_OBJECT.setRawResponse(data)
        self.RESPONSE_OBJECT.setError(False)
        self.RESPONSE_OBJECT.setCompleted(True)
        return self.RESPONSE_OBJECT
        
    def put(self, url, dataToSend, 
            contentType="application/json; charset=utf-8", 
            isJsonResponse = True, userAgent=USERNAME):
        """ PUT method used to send data to the Mu
        
        Arguments:
            url - the url used to retrieve data. it is combined with the 
            root url
            dataToSend - data to send to the Mu normaly in string of JSON 
            or XML data
            contentType - default is "application/json; charset=utf-8"
            isJsonResponse - is the response in the form of a json object
            userAgent - the userAgent to assign the reuest to. normaly the 
            same as the username used in the authentication
            
        Returns:
            MuResponseObject - Data is retrieved from Mu and stored in 
            MuResponseObject.
                COMPLETED - True if operation completed normaly
                ERROR - False if no error occured
                RAW_RESPONSE - Raw data from HTTP get request or URL error if 
                error is thrown
                UUID - contains Mu Job UUID if a UUID is found in XML response 
                from Mu
                JOBID - contains Mu Job ID if a Job Id is found in XML 
                response from Mu
                JSON_OBJCT - conains JSON data if isJsonResponse is true
                RETRIEVED_OBJECT - Fomrated Object of type specified in 
                RETRIEVED_OBJECT_TYPE
                RETRIEVED_OBJECT_TYPE - Can be JSON, XML, TXT, ERROR
                URL - contains text URL used in creation of urllib2.requst
                REQUST - contains urlib2.requst used in operation
                STATUS - JSON status if JSON response is found
                NAME - contains name value if JSON name or xml name data 
                is found
                MESSAGE - JSON message value if JSON response is found
            
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setUrl(url)
        req = urllib2.Request(url,dataToSend)
        req.get_method = lambda: 'PUT'
        req.add_header('Content-Type', contentType)
        req.add_header("User-Agent", userAgent)
        self.RESPONSE_OBJECT.setRequest(req)
        try:
            buffer = urllib2.urlopen(req)
            data = buffer.read()
            if isJsonResponse:
                try:
                    data = simplejson.loads(data)
                except:
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,False)
                    return self.RESPONSE_OBJECT
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
            log.debug(data)
        self.RESPONSE_OBJECT.setRawResponse(data)
        self.processResponse(data,self.RESPONSE_OBJECT,isJsonResponse)
        return self.RESPONSE_OBJECT
    
    def delete(self, url, isJsonResponse = False ,userAgent=USERNAME):
        """ DELETE method used to delte objects on the Mu
        
        Arguments:
            url - the url used to retrieve data. it is combined with the root 
            url
            isJsonResponse - is the response in the form of a json object
            userAgent - the userAgent to assign the reuest to. normaly the 
            same as the username used in the authentication
        
        Returns:
            MuResponseObject - Data is retrieved from Mu and stored in 
            MuResponseObject.
                COMPLETED - True if operation completed normaly
                ERROR - False if no error occured
                RAW_RESPONSE - Raw data from HTTP get request or URL error if 
                error is thrown
                UUID - contains Mu Job UUID if a UUID is found in XML response 
                from Mu
                JOBID - contains Mu Job ID if a Job Id is found in XML 
                response from Mu
                JSON_OBJCT - conains JSON data if isJsonResponse is true
                RETRIEVED_OBJECT - Fomrated Object of type specified in 
                RETRIEVED_OBJECT_TYPE
                RETRIEVED_OBJECT_TYPE - Can be JSON, XML, TXT, ERROR
                URL - contains text URL used in creation of urllib2.requst
                REQUST - contains urlib2.requst used in operation
                STATUS - JSON status if JSON response is found
                NAME - contains name value if JSON name or xml name data 
                is found
                MESSAGE - JSON message value if JSON response is found
            
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setUrl(url)
        req = urllib2.Request(url)
        req.get_method = lambda: 'DELETE'
        req.add_header("User-Agent", userAgent)
        self.RESPONSE_OBJECT.setRequest(req)
        try:
            buffer = urllib2.urlopen(req)
            data = buffer.read()
            if isJsonResponse:
                try:
                    data = simplejson.loads(data)
                except:
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,False)
                    return self.RESPONSE_OBJECT
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
            log.info(data)
        self.RESPONSE_OBJECT.setRawResponse(data)
        self.processResponse(data,self.RESPONSE_OBJECT,isJsonResponse)
        return self.RESPONSE_OBJECT

    def post(self, url, dataToSend=None,
             contentType='application/x-www-form-urlencoded', 
             isJsonResponse=False, userAgent=USERNAME):
        """ POST method used to send data to the Mu
        
        Arguments:
            url - the url used to retrieve data. it is combined with the 
            root url
            dataToSend - data to send to the Mu normaly in string of JSON 
            or XML data
            contentType - default is empty
            isJsonResponse - is the response in the form of a json object
            userAgent - the userAgent to assign the reuest to. normaly 
            the same as the username used in the authentication
        
        Returns:
            MuResponseObject - Data is retrieved from Mu and stored in 
            MuResponseObject.
                COMPLETED - True if operation completed normaly
                ERROR - False if no error occured
                RAW_RESPONSE - Raw data from HTTP get request or URL error 
                if error is thrown
                UUID - contains Mu Job UUID if a UUID is found in XML response 
                from Mu
                JOBID - contains Mu Job ID if a Job Id is found in XML 
                response from Mu
                JSON_OBJCT - conains JSON data if isJsonResponse is true
                RETRIEVED_OBJECT - Fomrated Object of type specified 
                in RETRIEVED_OBJECT_TYPE
                RETRIEVED_OBJECT_TYPE - Can be JSON, XML, TXT, ERROR
                URL - contains text URL used in creation of urllib2.requst
                REQUST - contains urlib2.requst used in operation
                STATUS - JSON status if JSON response is found
                NAME - contains name value if JSON name or xml name data is found
                MESSAGE - JSON message value if JSON response is found
        
        """
        log.debug("URL: %s ", url)
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setUrl(url)
        if dataToSend==None:
            req = urllib2.Request(url)
        else:
            req = urllib2.Request(url, dataToSend)
        log.debug("Post Data: %s ", dataToSend)
        req.get_method = lambda: 'POST'
        req.add_header('Content-Type', contentType)
        #req.add_header("User-Agent", userAgent)
        req.add_header("Keep-Alive", '115')
        #log.debug("Request Dir: %s", dir(req))
        log.debug("Request URL: %s", req.get_full_url())
        log.debug("Request Headers: %s", req.headers)
        self.RESPONSE_OBJECT.setRequest(req)
        log.debug("Passwd Mgr User: %s", 
            self.PASSWORD_MNGR.find_user_password(None,self.TOP_LEVEL_URL)[0])
        log.debug("Passwd Mgr Pwd Length: %d", 
            len(self.PASSWORD_MNGR.find_user_password(
            None,self.TOP_LEVEL_URL)[1]))
        try:
            buffer = urllib2.urlopen(req)
            log.debug("Response URL: %s ", buffer.geturl()) 
            log.debug("Response Headers: %s ", buffer.info()) 
            log.debug("Response Code: %s ", buffer.code)
            log.debug("Response Msg: %s ", buffer.msg)
            #log.debug("Response Dir: %s ", dir(buffer))
            if isJsonResponse:
                try:
                    data = buffer.read()
                    log.debug("Response Data: %s", data)
                    data = simplejson.loads(data)
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,True)
                except ValueError, e:
                    log.debug('ValueError: %s', e)
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,False)
                    return self.RESPONSE_OBJECT
            else:
                data = buffer.read()
                self.RESPONSE_OBJECT.setRawResponse(data)
                self.processResponse(data,self.RESPONSE_OBJECT,False)
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
            log.info(data)
            self.RESPONSE_OBJECT.setRawResponse(data)
            self.processResponse(data,self.RESPONSE_OBJECT,False)
        return self.RESPONSE_OBJECT
    
    def postFile(self,url,fileToSend,userAgent=USERNAME,contentType=''):
        """ POST method used to send data from a file to the Mu
        
        Arguments:
            url - the url used to retrieve data. it is combined with the root 
            url
            fileToSend - data to send to the Mu normaly in string of JSON 
            or XML
            userAgent - the userAgent to assign the reuest to. normaly the 
            same as the username used in the authentication
            contentType - default is empty
            
            Returns:
            MuResponseObject - Data is retrieved from Mu and stored 
            in MuResponseObject.
                COMPLETED - True if operation completed normaly
                ERROR - False if no error occured
                RAW_RESPONSE - Raw data from HTTP get request or URL error 
                if error is thrown
                UUID - contains Mu Job UUID if a UUID is found in XML response
                from Mu
                JOBID - contains Mu Job ID if a Job Id is found in XML response
                from Mu
                JSON_OBJCT - conains JSON data if isJsonResponse is true
                RETRIEVED_OBJECT - Fomrated Object of type specified 
                in RETRIEVED_OBJECT_TYPE
                RETRIEVED_OBJECT_TYPE - Can be JSON, XML, TXT, ERROR
                URL - contains text URL used in creation of urllib2.requst
                REQUST - contains urlib2.requst used in operation
                STATUS - JSON status if JSON response is found
                NAME - contains name value if JSON name or xml name data 
                is found
                MESSAGE - JSON message value if JSON response is found
            
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setUrl(url)
        log.debug("URL: %s ", url)
        try:
            fileToSend = open(fileToSend, 'r')
            dataToSend = fileToSend.read()
            log.debug("Post Data: %s ", dataToSend)
            req = urllib2.Request(url,dataToSend)
            req.add_header("User-Agent", userAgent)
            req.get_method = lambda: 'POST'
            self.RESPONSE_OBJECT.setRequest(req)
            buffer = urllib2.urlopen(req)
            data = buffer.read()
        except IOError, (errno, strerror):
            data =  "IOError: error " + str(errno) + ": " + strerror
            self.RESPONSE_OBJECT.setError(True)
            raise MuConnException
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
            self.RESPONSE_OBJECT.setError(True)
            raise MuConnException
        self.RESPONSE_OBJECT.setRawResponse(data)
        self.processResponse(data,self.RESPONSE_OBJECT)
        return self.RESPONSE_OBJECT    
        
    def postMime(self, url, fileToPost, mimeFieldName, mimeFieldValue, 
                 isJsonResponse=False, userAgent=USERNAME):
        """ POST method used to send data from a file to the Mu in a mime attachment
            
            Arguments:
            url - the url used to retrieve data. it is combined with the 
            root url
            fileToPost - data to send to the Mu normaly in string of JSON or XML
            mimeFieldName - the mime field name for the mime attachment
            mimeFieldValue - the mime filed value for the mime atatchment
            isJsonResponse - is expected repsone in a JSON format
            userAgent - the userAgent to assign the reuest to. normaly the 
            same as the username used in the authentication
            
            Returns:
            MuResponseObject - Data is retrieved from Mu and stored 
            in MuResponseObject.
               COMPLETED - True if operation completed normaly
               ERROR - False if no error occured
               RAW_RESPONSE - Raw data from HTTP get request or URL error 
               if error is thrown
               UUID - contains Mu Job UUID if a UUID is found in XML 
               response from Mu
               JOBID - contains Mu Job ID if a Job Id is found in XML 
               response from Mu
               JSON_OBJCT - conains JSON data if isJsonResponse is true
               RETRIEVED_OBJECT - Fomrated Object of type specified 
               in RETRIEVED_OBJECT_TYPE
               RETRIEVED_OBJECT_TYPE - Can be JSON, XML, TXT, ERROR
               URL - contains text URL used in creation of urllib2.requst
               REQUST - contains urlib2.requst used in operation
               STATUS - JSON status if JSON response is found
               NAME - contains name value if JSON name or xml name data is found
               MESSAGE - JSON message value if JSON response is found
            
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setUrl(url)
        try:
            form = MultiPartForm()
            # Add a file
            form.add_file(fileToPost, mimeFieldName, mimeFieldValue)
            # Build the request
            req = urllib2.Request(url)
            req.add_header('User-agent', userAgent)
            body = str(form)
            req.add_header('Content-type', form.get_content_type())
            req.add_header('Content-length', len(body))
            req.add_data(body)
        
            buffer = urllib2.urlopen(req)
            if isJsonResponse:
                try:
                    data = buffer.read()
                    data = simplejson.loads(data)
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,True)
                    return self.RESPONSE_OBJECT    
                except:
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,False)
                    return self.RESPONSE_OBJECT
            else:
                data = buffer.read()
                self.RESPONSE_OBJECT.setRawResponse(data)
                self.processResponse(data,self.RESPONSE_OBJECT,False)
                return self.RESPONSE_OBJECT    
        except IOError, (errno, strerror):
            data =  "IOError: error " + str(errno) + ": " + strerror
            self.RESPONSE_OBJECT.setRawResponse(data)
            self.processResponse(data,self.RESPONSE_OBJECT,False)
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
            self.RESPONSE_OBJECT.setRawResponse(data)
            self.processResponse(data,self.RESPONSE_OBJECT,False)
        return self.RESPONSE_OBJECT    

    def executeRequest(self,req,isJsonResponse=False):
        """ Used to execute a customized request 
        
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setRequest(req)
        try:
            log.debug(str(req))
            buffer = urllib2.urlopen(req)
            if isJsonResponse:
                try:
                    data = buffer.read()
                    data = simplejson.loads(data)
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,True)
                except:
                    self.RESPONSE_OBJECT.setRawResponse(data)
                    self.processResponse(data,self.RESPONSE_OBJECT,False)
                    return self.RESPONSE_OBJECT
            else:
                data = buffer.read()
                self.RESPONSE_OBJECT.setRawResponse(data)
                self.processResponse(data,self.RESPONSE_OBJECT,False)
        except urllib2.URLError, (err):
            data = "URL error(%s)" % (err)
            log.info(data)
            self.RESPONSE_OBJECT.setRawResponse(data)
            self.processResponse(data,self.RESPONSE_OBJECT,False)
        return self.RESPONSE_OBJECT

    def getRequest(self,type,url,dataToSend):
        """ Used to get a created request when customization is needed in 
        the request headers
        """
        if type=='POST':
            if dataToSend==None:
                req = urllib2.Request(url)
            else:
                req = urllib2.Request(url, dataToSend)
        elif type=='GET':
            req = urllib2.Request(url)
        else:
            log.info('unknown request method')
            req = None
        return req

    def getRequestBuffer(self, url, method='GET', userAgent=USERNAME,data='',
                         contentType="application/json; charset=utf-8"):
        """ Used to create a request buffer object using opener created 
        in MuConnection
            
        Arguments:
            url - the url used to retrieve data. it is combined with 
            the root url
            method - the http method to be used
            userAgent - the userAgent to assign the reuest to. normaly 
            the same as the username used in the authentication
            data - data to be sent with http method if any is sent beond 
            url request
            contentType - default is "application/json; charset=utf-8"
            
            Returns:
            urlib2 request buffer from urllib2.urlopen(request)
            
        """
        self.RESPONSE_OBJECT = MuResponseObject()
        self.RESPONSE_OBJECT.setUrl(url)
        if method == 'GET':
            req = urllib2.Request(url)
            req.add_header("User-Agent", userAgent)
            self.RESPONSE_OBJECT.setRequest(req)
            buffer = urllib2.urlopen(req)
            return buffer
        elif  method == 'PUT':
            req = urllib2.Request(url,data)
            req.get_method = lambda: 'PUT'
            req.add_header('Content-Type', contentType)
            req.add_header("User-Agent", userAgent)
            buffer = urllib2.urlopen(req)
            return buffer
        elif  method == 'POST':
            log.warn("Not IMPLEMENTED: method=POST")
            return
        else:
            log.warn("Not IMPLEMENTED")
            return

    def processResponse(self, rawData, muResponseObject, isJsonResponse=False):
        """ used to process the data returned from the Mu interactions 
        into a MuResponseObject.
        Parsing is done around 3 major groups: JSON, XML, and String. Each 
        type is parsed for common response obejcts that are returned from 
        the Mu API sessions
        
        Arguments:
            rawData - the raw data returned fromt he mu or the HTML error 
            code from an exception
            muResponseObject - the MuResponseObject to store the sorted data in
            isJsonResponse - is the raw data in JSON form
            
        Returns:
            MuResponseObject - Data is retrieved from Mu and stored 
            in MuResponseObject.
                COMPLETED - True if operation completed normaly
                ERROR - False if no error occured
                RAW_RESPONSE - Raw data from HTTP get request or URL error 
                if error is thrown
                UUID - contains Mu Job UUID if a UUID is found in XML 
                response from Mu
                JOBID - contains Mu Job ID if a Job Id is found in XML 
                response from Mu
                JSON_OBJCT - conains JSON data if isJsonResponse is true
                RETRIEVED_OBJECT - Fomrated Object of type specified 
                in RETRIEVED_OBJECT_TYPE
                RETRIEVED_OBJECT_TYPE - Can be JSON, XML, TXT, ERROR
                URL - contains text URL used in creation of urllib2.requst
                REQUST - contains urlib2.requst used in operation
                STATUS - JSON status if JSON response is found
                NAME - contains name value if JSON name or xml name 
                data is found
                MESSAGE - JSON message value if JSON response is found
            
        """
        if rawData == '':
            raise MuConnException, "Raw data is blank"
        muResponseObject.setRawResponse(rawData)
        muResponseObject.setCompleted(False)
        muResponseObject.setError(True)
        # process json
        log.debug("Raw Data: %s", rawData)
        if isJsonResponse:
            try:
                for key in rawData:
                    log.debug("Raw Data Key: %s", key	)
                    if key=="ERROR" or key=="ERROR_NAME" :
                        log.debug('MuConnection: in processResponse: ' 
                                  'found ERROR')
                        muResponseObject.setCompleted(False)
                        muResponseObject.setError(True)
                        muResponseObject.setRetrievedObjectType(
                            muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        raise MuConnException
                        break
                    elif key=="reason":
                        log.debug('MuConnection: in processResponse: ' 
                                  'found reason')
                        muResponseObject.setCompleted(False)
                        muResponseObject.setError(True)
                        message = rawData['reason']
                        muResponseObject.setStatus(False)
                        muResponseObject.setMessage(message)
                        muResponseObject.setRetrievedObjectType(
                            muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        raise MuConnException
                        break
                    elif key=="sessions":
                        log.debug('MuConnection: in processResponse: ' 
                                  'found sessions')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(
                            muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        break
                    elif key=='response':
                        log.debug('MuConnection: in processResponse: ' 
                                  'found response')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(
                            muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        data = rawData['response']
                        message = data['message']
                        status = data['status']
                        sessionId = data['sessionId']
                        muResponseObject.setMessage(message)
                        muResponseObject.setStatus(status)
                        return muResponseObject
                    elif key=='hosts':
                        log.debug('MuConnection: in processResponse: ' 
                                  'found hosts')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(
                            muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        break
                    elif key=='options':
                        log.debug('MuConnection: in processResponse: ' 
                                  'found options')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(
                            muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        break
                    elif key=='channels':
                        log.debug('MuConnection: in processResponse: ' 
                                  'found channels')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(
                            muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        break
                    elif key=='testResult':
                        log.debug('MuConnection: in processResponse: found testResult')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        data = rawData['testResult']
                        status = data['status']
                        muResponseObject.setStatus(status)
                        if status == "Failed":
                            message = data['testResult']
                            muResponseObject.setMessage(message)
                        break
                    elif key=='testResults':
                        log.debug('MuConnection: in processResponse: found testResults')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(muResponseObject.JSON)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        break
                    elif key=='statistics':
                        log.debug('MuConnection: in processResponse: found statistics')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(muResponseObject.SCALE_STATS)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setJsonObject(rawData)
                        break
                # exit for for loop if no break
                else:
                    log.debug('MuConnection: in processResponse: no break found in loop through JSON keys')
                    muResponseObject.setCompleted(True)
                    muResponseObject.setError(False)
                    muResponseObject.setRetrievedObjectType("UNKNOWN_JSON_RESPONSE")
                    muResponseObject.setRetrievedObject(rawData)
                    muResponseObject.setJsonObject(rawData)
            except:
                log.debug('MuConnection: in processResponse: exception thrown in json parsing')
                muResponseObject.setCompleted(True)
                muResponseObject.setError(False)
                muResponseObject.setRetrievedObjectType("UNKNOWN_JSON_EXCEPTION")    
        else:
            try:
                document = parseString(rawData)
                root = document.documentElement
                muResponseObject.setRawResponse(rawData)
                if root.tagName == 'job':
                    log.debug('MuConnection: in processResponse: found job xml tag')
                    muResponseObject.setJobId(root.getAttribute('id'))
                    muResponseObject.setName(root.getAttribute('name'))
                    myTree = ElementTree.XML(rawData)
                    for element in myTree:
                        if element.tag == "status":
                            muResponseObject.setStatus(element.text)
                            muResponseObject.setCompleted(True)
                            muResponseObject.setError(False)
                elif root.tagName == 'analysis':
                    log.debug('MuConnection: in processResponse: found analysis xml tag')
                    muResponseObject.setRetrievedObjectType(muResponseObject.XML)
                    muResponseObject.setUUID(root.getAttribute('uuid'))
                    myTree = ElementTree.XML(rawData)
                    for element in myTree:
                        if element.tag == "status":
                            muResponseObject.setStatus(element.text)
                        elif element.tag == "name":
                            muResponseObject.setName(element.text)
                    muResponseObject.setCompleted(True)
                    muResponseObject.setError(False)
                elif root.tagName == 'import_summary':
                    log.debug('MuConnection: in processResponse: found import_summary xml tag')
                    myTree = ElementTree.XML(rawData)
                    for element in myTree:
                        if element.tag == "model_object":
                            childList = element.getchildren()
                            for element2 in childList:
                                if element2.tag == "type":
                                    muResponseObject.setStatus(element2.text)
                                elif element2.tag == "name":
                                    muResponseObject.setName(element2.text)
                                elif element2.tag == "uuid":
                                    muResponseObject.setUUID(element2.text)
                            muResponseObject.setCompleted(True)
                            muResponseObject.setError(False)
                elif root.tagName == 'analysis_list':
                    log.debug('MuConnection: in processResponse: found analysis_list xml tag')
                    muResponseObject.setCompleted(True)
                    muResponseObject.setError(False)
                    muResponseObject.setRetrievedObjectType(muResponseObject.ANALYSIS_LIST)
                    muResponseObject.setRetrievedObject(rawData)
                elif root.tagName == 'fault_list_summary':
                    log.debug('MuConnection: in processResponse: found fault_list_summary xml tag')
                    muResponseObject.setCompleted(True)
                    muResponseObject.setError(False)
                    muResponseObject.setRetrievedObjectType(muResponseObject.FAULT_LIST_SUMMARY)
                    muResponseObject.setRetrievedObject(rawData)
                else:
                    log.debug('MuConnection: in processResponse: found UNKOWN xml tag\n'+rawData)
                    muResponseObject.setCompleted(False)
                    muResponseObject.setError(True)
                    raise MuConnException
            except:
                if isinstance(rawData, str):
                    if rawData.find("URL error")>=0:
                        log.debug('MuConnection: in processResponse: found URL error text ')
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setRetrievedObjectType(muResponseObject.URL_ERROR)
                        muResponseObject.setCompleted(False)
                        muResponseObject.setError(True)    
                        raise MuConnException
                    elif (rawData.find("updated")>=0) or (rawData.find("created")>=0) or (rawData.find("deleted")>=0):
                        log.debug('MuConnection: in processResponse: found basic response text: updated, created, or deleted')
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(muResponseObject.TEXT)
                        muResponseObject.setRetrievedObject(rawData)
                    elif muResponseObject.getUrl().find("/templates/types")>=0:
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                    elif muResponseObject.getUrl().startswith('["')>=0:
                        muResponseObject.setCompleted(True)
                        muResponseObject.setError(False)
                        muResponseObject.setRetrievedObjectType(muResponseObject.TEXT)
                        muResponseObject.setRetrievedObject(rawData)
                        muResponseObject.setRawResponse(rawData)
                    else:
                        log.debug('MuConnection: in processResponse: found unknown response or response id not implemeted')
                        log.debug('Raw Response: %s', rawData)
                        muResponseObject.setCompleted(False)
                        muResponseObject.setError(True)
                        muResponseObject.setRetrievedObjectType("UNKNOWN")
                        muResponseObject.setRetrievedObject(rawData)
                        raise MuConnException
                elif isinstance(rawData, MuResponseObject):
                    log.debug('MuConnection: in processResponse: found MuResponseObject')
                    muResponseObject.setCompleted(True)
                    muResponseObject.setError(False)
                    muResponseObject.setRetrievedObjectType(muResponseObject.TEXT_ARAY)
                    tmpList = rawData.split("\n")
                    muResponseObject.setRetrievedObject(tmpList)
                else:
                    log.debug('MuConnection: in processResponse: found unknown object response or response object is not implemeted')
                    muResponseObject.setCompleted(False)
                    muResponseObject.setError(True)
                    muResponseObject.setRetrievedObjectType("UNKNOWN")
                    muResponseObject.setRetrievedObject(rawData)
                    raise MuConnException
        return muResponseObject
        

class MuResponseObject:

    ''' MuResponseObject: This class is instantiated to store data returned 
    from a Mu API session. 
    It is meant to insolate any Automation development by Mu users from 
    changes in the raw Mu API.
    
        Arguments: 
        None
    
        Globals:
        ---------------------------------------------------------------------
        These variables are constants used for defining RETRIEVED_OBJECT_TYPE
        JSON = "JSON" - JSON formated object
        FILE = "FILE" - File name including path
        XML = "XML" - XML string data
        TEXT = "TEXT" - String object
        TEXT_ARAY = "TEXT_ARAY" - List of strings
        URL_ERROR = "URL_ERROR" - string data indicating what URL Error 
        was thrown
        ANALYSIS_LIST = list of XML analysis responses in one string object
        FAULT_LIST_SUMMARY = "FAULT_LIST_SUMMARY" - list of Faults in XML 
        fomr in one strng object
        FAULT_LIST = "FAULT_LIST" - list of MuRestApi.Fault objects
        RESULTS_LIST = "RESULTS_LIST" - list of MuTestApi.Result objects
        LIST = "LIST" - a pyhton list of objects
        ---------------------------------------------------------------------
        These variables are used for storing data returned from the Mu API 
        session
        COMPLETED - boolan indicating if the operation completed with 
        expected result
        ERROR - boolean indicating if an error occured in the operation
        RAW_RESPONSE - raw data or URL error returned from Mu API session
        UUID - uuid from returned json or xml data
        JOBID - jobid from returned json or xml data
        JSON_OBJCT - json object returned from Mu api session
        RETRIEVED_OBJECT - formated object normaly created from the data 
        contained in the RAW_RESPONSE
        RETRIEVED_OBJECT_TYPE - Type of data to be found in RETRIEVED_OBJECT
        URL - url used in the Mu API operation
        REQUST - request used in the urllib2 operation
        STATUS - status value returned in the JSON or XML data
        NAME - name value specified in the JSON or XML data 
        MESSAGE - a JSON message response value if one is found
        ---------------------------------------------------------------------
    
        TODO:
    
    '''

    def __str__(self):
        """ used to print MuResponseObject in some maner of order
            
            Arguments:
            none
            
            Returns:
            String
            
        """

        data = "COMPLETED = " + str(self.COMPLETED) + "\n"
        data = data + "ERROR = " + str(self.ERROR) + "\n"
        data = data + "STATUS = " + str(self.STATUS) + "\n"
        data = data + "MESSAGE = " + str(self.MESSAGE) + "\n"
        data = data + "NAME = " + self.NAME + "\n"
        data = data + "UUID = " + self.UUID + "\n"
        data = data + "JOBID = " + self.JOBID + "\n"
        data = data + "JSON_OBJCT = " + str(self.JSON_OBJCT) + "\n"
        data = data + "RETRIEVED_OBJECT = " + str(self.RETRIEVED_OBJECT) + \
            "\n"
        data = data + "RETRIEVED_OBJECT_TYPE = " + self.RETRIEVED_OBJECT_TYPE + \
            "\n"
        data = data + "URL = " + self.URL + "\n"
        data = data + "REQUST = " + str(self.REQUST) + "\n"
        return data

    JSON = "JSON"
    FILE = "FILE"
    XML = "XML"

    TEXT = "TEXT"
    TEXT_ARAY = "TEXT_ARAY"
    URL_ERROR = "URL_ERROR"
    ANALYSIS_LIST = "ANALYSIS_LIST"
    FAULT_LIST_SUMMARY = "FAULT_LIST_SUMMARY"
    FAULT_LIST = "FAULT_LIST"
    RESULTS_LIST = "RESULTS_LIST"
    LIST = "LIST"
    SCALE_STATS = "SCALE_STATS"

    COMPLETED = False
    ERROR = False
    RAW_RESPONSE = ''
    UUID = ''
    JOBID = ''
    JSON_OBJCT = ''
    RETRIEVED_OBJECT = []
    RETRIEVED_OBJECT_TYPE = ''
    URL = ''
    REQUST = ''
    STATUS = None
    NAME = ''
    MESSAGE = ''

    def getRawResponse(self):
        return self.RAW_RESPONSE

    def getRetrievedObject(self):
        return self.RETRIEVED_OBJECT

    def getRetrievedObjectType(self):
        return self.RETRIEVED_OBJECT_TYPE

    def getJsonObject(self):
        return self.JSON_OBJCT

    def getUrl(self):
        return self.URL

    def getRequest(self):
        return self.REQUST

    def getUUID(self):
        return self.UUID

    def getJobid(self):
        return self.JOBID

    def isCompleted(self):
        return self.COMPLETED

    def hasError(self):
        return self.ERROR

    def getStatus(self):
        return self.STATUS

    def getName(self):
        return self.NAME

    def getMessage(self):
        return self.MESSAGE

    def setRawResponse(self, obj):
        self.RAW_RESPONSE = obj

    def setUUID(self, obj):
        self.UUID = obj

    def setJobId(self, obj):
        self.JOBID = obj

    def setRetrievedObject(self, obj):
        self.RETRIEVED_OBJECT = obj

    def setRetrievedObjectType(self, obj):
        self.RETRIEVED_OBJECT_TYPE = obj

    def setJsonObject(self, obj):
        self.JSON_OBJCT = obj

    def setUrl(self, obj):
        self.URL = obj

    def setRequest(self, obj):
        self.REQUST = obj

    def setCompleted(self, obj):
        self.COMPLETED = obj

    def setError(self, obj):
        self.ERROR = obj

    def setStatus(self, obj):
        self.STATUS = obj

    def setName(self, obj):
        self.NAME = obj

    def setMessage(self, obj):
        self.MESSAGE = obj


class MultiPartForm(object):

    """ Accumulate the data to be used when posting a form for mime attachment.
    The code below was found on an anonymous posting on a web site that I 
    cant remember so thank you to whomever wrote it.
    """

    def __init__(self):
        self.form_fields = []
        self.files = []
        self.boundary = mimetools.choose_boundary()
        return

    def get_content_type(self):
        return 'multipart/form-data; boundary=%s' % self.boundary

    def add_field(self, name, value):
        """Add a simple field to the form data."""

        self.form_fields.append((name, value))
        return

    def add_file(self, fileName, fieldName, fieldValue, mimetype=None):
        """Add a file to be uploaded."""

        fileToSend = open(fileName, 'r')
        body = fileToSend.read()
        if mimetype is None:
            mimetype = mimetypes.guess_type(fileName)[0] or \
                'application/octet-stream'
        self.files.append((fieldName, fieldValue, mimetype, body))
        return True

    def __str__(self):
        """Return a string representing the form data, including attached 
        files.
        """

        parts = []
        part_boundary = '--' + self.boundary

        parts.extend([part_boundary,
                     'Content-Disposition: form-data; name="%s"' % name,
                     '', value] for (name, value) in self.form_fields)

        parts.extend([part_boundary,
                     'Content-Disposition: file; name="%s"; filename="%s"' %
                     (field_name, filename), 'Content-Type: %s' %
                     content_type, '', body] for (field_name, filename,
                     content_type, body) in self.files)

        flattened = list(itertools.chain(*parts))
        flattened.append('--' + self.boundary + '--')
        flattened.append('')
        return ('\r\n').join(flattened)


